{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "978df5ba-ef41-4db7-8de5-22b719f4e264",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "import string\n",
    "import pandas as pd\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.cluster import KMeans\n",
    "from nltk.probability import FreqDist\n",
    "from nltk.corpus import stopwords\n",
    "from sentence_transformers import SentenceTransformer\n",
    "from sklearn.metrics import classification_report"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f33e7736-f8ac-48d2-9abf-fa0e46adafaa",
   "metadata": {},
   "outputs": [],
   "source": [
    "%run -i \"../util/lang_utils.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c02b3139-2f1c-478c-a286-323b359d4bf7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "           category                                               text\n",
      "0              tech  tv future in the hands of viewers with home th...\n",
      "1          business  worldcom boss  left books alone  former worldc...\n",
      "2             sport  tigers wary of farrell  gamble  leicester say ...\n",
      "3             sport  yeading face newcastle in fa cup premiership s...\n",
      "4     entertainment  ocean s twelve raids box office ocean s twelve...\n",
      "...             ...                                                ...\n",
      "2220       business  cars pull down us retail figures us retail sal...\n",
      "2221       politics  kilroy unveils immigration policy ex-chatshow ...\n",
      "2222  entertainment  rem announce new glasgow concert us band rem h...\n",
      "2223       politics  how political squabbles snowball it s become c...\n",
      "2224          sport  souness delight at euro progress boss graeme s...\n",
      "\n",
      "[2225 rows x 2 columns]\n"
     ]
    }
   ],
   "source": [
    "bbc_df = pd.read_csv(\"../data/bbc-text.csv\")\n",
    "print(bbc_df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cc326312-04e0-474f-8c8f-c0e0493bbdeb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2002\n",
      "223\n"
     ]
    }
   ],
   "source": [
    "bbc_train, bbc_test = train_test_split(bbc_df, test_size=0.1)\n",
    "print(len(bbc_train))\n",
    "print(len(bbc_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c5027dce-1bd0-4b2d-bb40-1978d8fd5470",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {color: black;}#sk-container-id-1 pre{padding: 0;}#sk-container-id-1 div.sk-toggleable {background-color: white;}#sk-container-id-1 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-1 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-1 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-1 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-1 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-1 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-1 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-1 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-1 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-1 div.sk-item {position: relative;z-index: 1;}#sk-container-id-1 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-1 div.sk-item::before, #sk-container-id-1 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-1 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-1 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-1 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-1 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-1 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-1 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-1 div.sk-label-container {text-align: center;}#sk-container-id-1 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-1 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KMeans(n_clusters=5, n_init=&#x27;auto&#x27;)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">KMeans</label><div class=\"sk-toggleable__content\"><pre>KMeans(n_clusters=5, n_init=&#x27;auto&#x27;)</pre></div></div></div></div></div>"
      ],
      "text/plain": [
       "KMeans(n_clusters=5, n_init='auto')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "documents = bbc_train['text'].values\n",
    "model = SentenceTransformer('all-MiniLM-L6-v2')\n",
    "encoded_data = model.encode(documents)\n",
    "km = KMeans(n_clusters=5, n_init='auto', init='k-means++')\n",
    "km.fit(encoded_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d8baba74-34db-428e-b0cd-228708e11a81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "['said', 'people', 'new', 'also', 'mr', 'technology', 'would', 'one', 'mobile', 'could', 'users', 'music', 'use', 'software', 'us', 'net', 'digital', 'many', 'games', 'year', 'phone', 'uk', 'make', 'like', 'computer', 'service', 'get', 'world', 'time', 'online', 'internet', 'used', 'way', 'microsoft', 'broadband', 'tv', 'information', 'video', 'data', 'million', 'first', 'using', 'system', 'services', 'phones', 'security', 'two', 'number', 'says', 'work', 'firms', 'search', 'market', 'companies', 'industry', 'firm', 'content', 'game', 'according', 'last', 'media', 'much', 'networks', 'next', 'access', 'news', 'take', 'apple', 'research', 'network', 'around', 'web', 'pc', 'want', 'well', 'years', 'home', 'even', 'already', 'help', 'sony', 'made', 'company', 'going', 'site', 'sites', 'e-mail', 'see', 'players', 'devices', 'website', 'set', 'bbc', 'bt', 'different', 'radio', 'need', 'go', 'customers', 'consumers', 'found', 'able', 'europe', 'show', 'part', 'end', 'gaming', 'months', 'available', 'added', 'report', 'told', 'future', 'still', 'systems', 'may', 'play', 'windows', 'download', 'messages', 'control', 'programs', 'sales', 'every', 'google', 'become', 'mobiles', 'top', 'virus', 'find', 'technologies', 'currently', 'popular', 'three', 'five', 'spam', 'websites', 'dvd', 'version', 'files', 'means', 'say', 'big', 'free', 'european', 'personal', 'pcs', 'wireless', 'hard', 'gadgets', 'program', 'via', 'likely', 'without', 'good', 'computers', 'latest', 'legal', 'business', 'put', 'images', 'really', 'consumer', 'move', 'since', 'offer', 'problem', 'month', 'called', 'device', 'gadget', 'group', 'look', 'getting', 'second', 'attacks', 'player', 'come', 'although', 'another', 'making', 'machines', 'machine', 'calls', 'looking', 'released', 'working', 'real', 'back', 'better', 'money', 'day', 'current', 'high-definition', 'power', 'small', 'less', 'know', 'almost', 'might']\n",
      "1\n",
      "['said', 'game', 'england', 'first', 'win', 'world', 'last', 'would', 'one', 'two', 'time', 'back', 'also', 'players', 'play', 'new', 'cup', 'wales', 'year', 'good', 'side', 'ireland', 'team', 'second', 'match', 'could', 'six', 'set', 'club', 'final', 'coach', 'france', 'three', 'rugby', 'season', 'get', 'made', 'think', 'us', 'injury', 'added', 'chelsea', 'well', 'take', 'great', 'going', 'best', 'go', 'got', 'victory', 'open', 'years', 'like', 'told', 'next', 'nations', 'games', 'league', 'make', 'way', 'player', 'start', 'champion', 'minutes', 'played', 'williams', 'since', 'united', 'still', 'liverpool', 'scotland', 'four', 'international', 'arsenal', 'playing', 'come', 'title', 'lot', 'want', 'right', 'beat', 'chance', 'five', 'really', 'try', 'sport', 'olympic', 'home', 'end', 'number', 'former', 'another', 'roddick', 'top', 'ball', 'know', 'week', 'race', 'bbc', 'squad', 'third', 'put', 'football', 'robinson', 'winning', 'see', 'european', 'v', 'saturday', 'manager', 'took', 'came', 'italy', 'goal', 'mark', 'grand', 'away', 'lost', 'left', 'return', 'place', 'half', 'record', 'decision', 'points', 'captain', 'much', 'champions', 'andy', 'australian', 'ahead', 'break', 'sunday', 'face', 'round', 'boss', 'better', 'run', 'premiership', 'real', 'even', 'athens', 'seed', 'lead', 'people', 'madrid', 'big', 'zealand', 'jones', 'indoor', 'forward', 'gerrard', 'never', 'went', 'give', 'britain', 'women', 'j', 'early', 'defeat', 'championships', 'summer', 'move', 'despite', 'training', 'penalty', 'career', 'newcastle', 'days', 'given', 'hard', 'manchester', 'slam', 'irish', 'test', 'spain', 'part', 'admitted', 'referee', 'weeks', 'tournament', 'pressure', 'work', 'fourth', 'british', 'form', 'long', 'men', 'event', 'country', 'matches', 'bit', 'looking', 'tennis', 'missed', 'french', 'day', 'g', 'lions', 'line']\n",
      "2\n",
      "['said', 'film', 'best', 'music', 'also', 'year', 'us', 'one', 'new', 'awards', 'show', 'award', 'last', 'first', 'years', 'number', 'star', 'director', 'actor', 'people', 'band', 'uk', 'mr', 'two', 'top', 'would', 'tv', 'british', 'album', 'bbc', 'films', 'three', 'time', 'including', 'song', 'actress', 'world', 'festival', 'million', 'made', 'prize', 'singer', 'like', 'stars', 'comedy', 'oscar', 'well', 'record', 'series', 'rock', 'life', 'london', 'movie', 'make', 'win', 'took', 'get', 'hit', 'game', 'role', 'ceremony', 'aviator', 'office', 'could', 'five', 'musical', 'told', 'added', 'nominations', 'set', 'theatre', 'week', 'good', 'play', 'four', 'many', 'second', 'think', 'group', 'released', 'place', 'hollywood', 'man', 'box', 'include', 'take', 'radio', 'day', 'night', 'pop', 'named', 'starring', 'drama', 'nominated', 'love', 'success', 'chart', 'single', 'great', 'went', 'category', 'work', 'book', 'television', 'february', 'going', 'came', 'performance', 'academy', 'among', 'career', 'really', 'big', 'company', 'since', 'oscars', 'fans', 'home', 'later', 'go', 'played', 'ray', 'industry', 'part', 'christmas', 'black', 'children', 'become', 'around', 'next', 'may', 'young', 'original', 'back', 'artists', 'american', 'sold', 'list', 'live', 'act', 'says', 'songs', 'charles', 'john', 'former', 'taking', 'third', 'sales', 'family', 'stage', 'story', 'winners', 'money', 'see', 'way', 'martin', 'release', 'urban', 'winner', 'shows', 'favourite', 'baby', 'want', 'got', 'michael', 'audience', 'still', 'included', 'programme', 'end', 'know', 'never', 'golden', 'died', 'tour', 'debut', 'public', 'foxx', 'version', 'much', 'biggest', 'received', 'held', 'however', 'production', 'seen', 'york', 'woman', 'angeles', 'right', 'international', 'jackson', 'due', 'stone', 'according', 'special', 'come', 'news', 'following', 'producer']\n",
      "3\n",
      "['said', 'mr', 'would', 'labour', 'government', 'people', 'blair', 'party', 'election', 'also', 'minister', 'new', 'could', 'brown', 'told', 'uk', 'howard', 'public', 'plans', 'one', 'prime', 'tory', 'say', 'chancellor', 'tax', 'secretary', 'leader', 'bbc', 'lord', 'britain', 'home', 'general', 'tories', 'next', 'year', 'says', 'make', 'campaign', 'tony', 'time', 'get', 'last', 'mps', 'years', 'lib', 'police', 'spokesman', 'law', 'made', 'two', 'may', 'country', 'first', 'added', 'liberal', 'us', 'issue', 'saying', 'michael', 'take', 'work', 'british', 'way', 'bill', 'vote', 'local', 'like', 'think', 'going', 'political', 'house', 'kennedy', 'back', 'week', 'want', 'rights', 'many', 'part', 'council', 'system', 'parties', 'expected', 'conservative', 'office', 'immigration', 'war', 'lords', 'voters', 'services', 'ministers', 'see', 'support', 'commons', 'put', 'iraq', 'right', 'mp', 'claims', 'go', 'without', 'ukip', 'former', 'london', 'parliament', 'set', 'foreign', 'much', 'whether', 'believe', 'blunkett', 'report', 'gordon', 'news', 'world', 'men', 'day', 'committee', 'still', 'conservatives', 'kilroy-silk', 'human', 'come', 'children', 'dems', 'even', 'good', 'eu', 'already', 'spending', 'need', 'help', 'well', 'change', 'health', 'charles', 'budget', 'money', 'issues', 'programme', 'called', 'increase', 'david', 'taxes', 'clear', 'national', 'plan', 'european', 'pay', 'cabinet', 'policy', 'economy', 'legal', 'since', 'wales', 'education', 'asylum', 'number', 'place', 'asked', 'politics', 'clarke', 'democrats', 'service', 'today', 'decision', 'chief', 'shadow', 'three', 'give', 'court', 'role', 'members', 'women', 'must', 'case', 'conference', 'countries', 'used', 'england', 'meeting', 'economic', 'radio', 'four', 'action', 'debate', 'proposals', 'use', 'monday', 'stand', 'ms', 'dem', 'away', 'held', 'john', 'schools', 'scotland', 'working', 'rules', 'later', 'speech']\n",
      "4\n",
      "['said', 'us', 'year', 'mr', 'would', 'also', 'market', 'company', 'new', 'growth', 'firm', 'economy', 'government', 'last', 'bank', 'sales', 'could', 'economic', 'oil', 'shares', 'china', 'prices', 'years', 'however', 'may', 'chief', 'two', 'one', 'world', 'analysts', 'deal', 'business', 'since', 'yukos', 'financial', 'expected', 'group', 'uk', 'rise', 'firms', 'companies', 'dollar', 'country', 'stock', 'many', 'december', 'first', 'months', 'trade', 'people', 'still', 'president', 'three', 'interest', 'european', 'rate', 'investment', 'profits', 'time', 'india', 'told', 'quarter', 'foreign', 'state', 'executive', 'strong', 'news', 'month', 'demand', 'made', 'rates', 'countries', 'figures', 'biggest', 'tax', 'added', 'euros', 'much', 'spending', 'bid', 'back', 'offer', 'jobs', 'rose', 'high', 'price', 'investors', 'costs', 'increase', 'exchange', 'eu', 'london', 'january', 'part', 'money', 'week', 'europe', 'next', 'according', 'deutsche', 'share', 'industry', 'recent', 'fall', 'set', 'court', 'hit', 'move', 'report', 'russian', 'million', 'production', 'debt', 'cut', 'pay', 'make', 'japan', 'former', 'fell', 'well', 'take', 'despite', 'budget', 'minister', 'annual', 'russia', 'reported', 'number', 'bankruptcy', 'says', 'markets', 'likely', 'say', 'sale', 'international', 'higher', 'consumer', 'cost', 'exports', 'trading', 'shareholders', 'continue', 'end', 'lse', 'united', 'public', 'earlier', 'future', 'case', 'lost', 'south', 'deficit', 'fraud', 'plans', 'profit', 'decision', 'already', 'global', 'seen', 'record', 'giant', 'less', 'euro', 'sector', 'put', 'agreed', 'november', 'although', 'unit', 'main', 'largest', 'came', 'banks', 'low', 'buy', 'tuesday', 'warned', 'help', 'takeover', 'previous', 'meeting', 'german', 'stake', 'national', 'finance', 'house', 'boerse', 'boost', 'car', 'general', 'airline', 'past', 'need', 'including', 'work', 'see', 'gazprom', 'statement', 'indian', 'value']\n"
     ]
    }
   ],
   "source": [
    "print_most_common_words_by_cluster(documents, km, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "13c44d80-d9dc-4d70-8dd3-7178d80b38dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "           category                                               text  \\\n",
      "578           sport  isinbayeva heads for birmingham olympic pole v...   \n",
      "2012  entertainment  boogeyman takes box office lead the low-budget...   \n",
      "208   entertainment  public show for reynolds portrait sir joshua r...   \n",
      "1078       politics  stalemate in pension strike talks talks aimed ...   \n",
      "1229          sport  newcastle 2-1 bolton kieron dyer smashed home ...   \n",
      "...             ...                                                ...   \n",
      "1977       politics  row over  police  power for csos the police fe...   \n",
      "1218       politics  whitehall cuts  ahead of target  thousands of ...   \n",
      "880   entertainment  roundabout continues nostalgia trip the new bi...   \n",
      "1552           tech  attack prompts bush site block the official re...   \n",
      "278        business  soaring oil  hits world economy  the soaring c...   \n",
      "\n",
      "      prediction  \n",
      "578            1  \n",
      "2012           2  \n",
      "208            2  \n",
      "1078           3  \n",
      "1229           1  \n",
      "...          ...  \n",
      "1977           3  \n",
      "1218           3  \n",
      "880            2  \n",
      "1552           0  \n",
      "278            4  \n",
      "\n",
      "[223 rows x 3 columns]\n"
     ]
    }
   ],
   "source": [
    "bbc_test[\"prediction\"] = bbc_test[\"text\"].apply(lambda x: km.predict(model.encode([x]))[0])\n",
    "print(bbc_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "9630290c-3201-41ca-b342-c708ef73683e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "               precision    recall  f1-score   support\n",
      "\n",
      "     business       0.98      0.96      0.97        55\n",
      "entertainment       0.95      1.00      0.97        38\n",
      "     politics       0.97      0.93      0.95        42\n",
      "        sport       0.98      0.96      0.97        47\n",
      "         tech       0.93      0.98      0.95        41\n",
      "\n",
      "     accuracy                           0.96       223\n",
      "    macro avg       0.96      0.97      0.96       223\n",
      " weighted avg       0.96      0.96      0.96       223\n",
      "\n"
     ]
    }
   ],
   "source": [
    "topic_mapping = {0:\"tech\", 1:\"sport\", 2:\"entertainment\", 3:\"politics\", 4:\"business\"}\n",
    "bbc_test[\"pred_category\"] = bbc_test[\"prediction\"].apply(lambda x: topic_mapping[x])\n",
    "print(classification_report(bbc_test[\"category\"], bbc_test[\"pred_category\"]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d4b449c6-64c5-4f3c-a37e-5c0c2a8d7621",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_example = \"\"\"Manchester United players slumped to the turf \n",
    "at full-time in Germany on Tuesday in acknowledgement of what their \n",
    "latest pedestrian first-half display had cost them. The 3-2 loss at \n",
    "RB Leipzig means United will not be one of the 16 teams in the draw \n",
    "for the knockout stages of the Champions League. And this is not the \n",
    "only price for failure. The damage will be felt in the accounts, in \n",
    "the dealings they have with current and potentially future players \n",
    "and in the faith the fans have placed in manager Ole Gunnar Solskjaer. \n",
    "With Paul Pogba's agent angling for a move for his client and ex-United \n",
    "defender Phil Neville speaking of a \"witchhunt\" against his former team-mate \n",
    "Solskjaer, BBC Sport looks at the ramifications and reaction to a big loss for United.\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8fd317a7-e945-4736-9405-c1121612e05f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "predictions = km.predict(model.encode([new_example]))\n",
    "print(predictions[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04f9ac17-5793-42cc-a1a5-dcec436f6bab",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
